package ru.omsu.diveintoandroid.funnycats.scenes;

import org.cocos2d.actions.UpdateCallback;
import org.cocos2d.layers.CCLayer;
import org.cocos2d.layers.CCScene;
import org.cocos2d.nodes.CCDirector;
import org.cocos2d.nodes.CCLabel;
import org.cocos2d.nodes.CCSprite;
import org.cocos2d.nodes.CCTextureCache;
import org.cocos2d.opengl.CCTexture2D;
import org.cocos2d.types.CGPoint;
import org.cocos2d.types.CGRect;
import org.cocos2d.types.CGSize;
import org.cocos2d.types.ccColor3B;
import org.gtugs.omsk.funnycats.ui.GameActivity;

import ru.omsu.diveintoandroid.funnycats.engine.ExtraObject;
import ru.omsu.diveintoandroid.funnycats.engine.GameObject;
import ru.omsu.diveintoandroid.funnycats.engine.GameWorld;
import ru.omsu.diveintoandroid.funnycats.engine.PrototypeObject;
import android.util.Log;
import android.view.MotionEvent;

public class GameScene extends CCLayer implements UpdateCallback {
	
	public static CCScene scene() {
		CCScene scene = CCScene.node();
		CCSprite bg = CCSprite.sprite("background.png");
		bg.setPosition(CCDirector.sharedDirector().winSize().width/2, CCDirector.sharedDirector().winSize().height/2);
		scene.addChild(bg);
		CCLayer layer = new GameScene();
		scene.addChild(layer);
		return scene;
	}

	public GameScene(){
		Log.d(TAG, "game scene constructor");
		setIsTouchEnabled(true);
		setIsKeyEnabled(true);
		initScene();
		scheduleUpdate();
	}

	protected void initScene() {
		initTextures();
		initWorld();
		initSprites();

		initConst();		
		initLabel();
	}
	
	protected void initConst() {
		lastTime = 2*System.currentTimeMillis();//Maybe another way
		extraRecharge = 0;
	}
	
	protected void initLabel() {
		lbl = CCLabel.makeLabel("Points: 0", "DroidSans", 36);
		lbl.setColor(ccColor3B.ccORANGE);
		lbl.setPosition(CGPoint.ccp(100, CCDirector.sharedDirector().winSize().height - 30));
		addChild(lbl);
	}
	
	protected void initTextures(){
		catRedRightTexture	= CCTextureCache.sharedTextureCache().addImage("cat_red_right.png");
		catRedLeftTexture	= CCTextureCache.sharedTextureCache().addImage("cat_red_left.png");
		mouseTexture	= CCTextureCache.sharedTextureCache().addImage("mouse.png");
		milkTexture 	= CCTextureCache.sharedTextureCache().addImage("whiskas.png");
	}
	
	protected void initPoint() {
        CGSize display_size = CCDirector.sharedDirector().winSize();

    	if(display_size.width/display_size.height > 4f/3){
    		pointToWorld.x = (display_size.width - display_size.height * 4/3)/2;
        }else{
        	pointToWorld.y = (display_size.height - display_size.width * 3/4)/2;
        }        
	}
	
	protected void initRatio() {
        CGSize display_size = CCDirector.sharedDirector().winSize();

    	if(display_size.width/display_size.height > 4f/3){
    		PTM_RATIO = display_size.height / ROOM_HEIGHT;
        }else{
        	PTM_RATIO = display_size.width / ROOM_WIDTH;
        }		
	}
	
	protected void initWorld(){
		initPoint();
		initRatio();

		world = new GameWorld(CGSize.make(ROOM_WIDTH, ROOM_HEIGHT), this);
		initGameObjects();
	}

	protected void initGameObjects(){
		mouse = makeObject(MOUSE_WIDTH, mouseTexture);
		world.addStaticObject(mouse);
		mouse.setPosition(CGPoint.ccp(1.f, 1.f));
		
		for(int i=0;i< NUMBER_OF_CATS ;i++){
			GameObject cat = makeObject(CAT_WIDTH, catRedRightTexture);
			cat.setSpeed(CGPoint.ccp((float)Math.random() - 0.5f, (float)Math.random() - 0.5f));
			world.addDynamicObject(cat);
		}		
	}
		
	protected GameObject makeObject(float width, CCTexture2D texture){
		CGSize objectSize = CGSize.make(width, width * getTextureRelation(texture));
		GameObject object = new GameObject(CCSprite.sprite(texture), objectSize, world.relativePosition(0.5f, 0.5f));
		return object;
	}
	
	protected ExtraObject makeExtraObject(float width, CCTexture2D texture){
		CGSize objectSize = CGSize.make(width, width * getTextureRelation(texture));
		ExtraObject object = new ExtraObject(CCSprite.sprite(texture), objectSize, world.relativePosition(0.5f, 0.5f));
		return object;
	}

	protected void initSprites(){
		for(PrototypeObject object: world.getObjects()){
			CCSprite sprite = object.getSprite();
			sprite.setPosition(worldPointToLayerPoint(object.getPosition()));
		}
	}
	
	@Override
	public void update(float d) {		
		if(isFinish()){
			//to do something
			CCDirector.sharedDirector().runWithScene(GameScene.scene());
			return;
		}
		
//		world.IsContactWithExtraObject(mouse);
		
		long currentTime = System.currentTimeMillis();
		
		if(currentTime > lastTime){
			long dt = currentTime - lastTime;
			
			updateScore(dt);
			world.step(dt);

			if((currentTime - extraRecharge) > extraTime){
				if(world.isPossibleAddExtraObjects()){
					world.increaseSpeed();
					createExtraObject();
					extraRecharge = currentTime;
				}
			}
			
			moveSprites();
		}
		
		lastTime = currentTime;
	}
	
	protected void moveSprites() {
		for(GameObject object: world.getDynamicObjects()){
			object.getSprite().setPosition(worldPointToLayerPoint(object.getPosition()));
			setAppropriateSprite(object);
		}		
	}
	
	protected void setAppropriateSprite(GameObject object) {
		if(object.getSpeed().x >= 0){
			checkObjectTexture(object, catRedRightTexture);
		} else{
			checkObjectTexture(object, catRedLeftTexture);
		}		
	}
	
	protected void updateScore(long dt){
		SCORE += dt;
		lbl.setString("Points: " + Long.toString(SCORE/1000));
	}
	
	protected boolean isFinish(){
		return world.IsSomeContactExist(mouse);
	}

	protected void createExtraObject(){
		ExtraObject milk = makeExtraObject(BOTTLE_WIDTH, milkTexture);
		milk.setPosition(CGPoint.ccp(ROOM_WIDTH*(float)Math.random(),ROOM_HEIGHT*(float)Math.random()));
		milk.getSprite().setPosition(worldPointToLayerPoint(milk.getPosition()));

		world.addExtraObject(milk);
	}
	
	protected float getTextureRelation(CCTexture2D texture){
		return texture.getHeight() / texture.getWidth();
	}
	
	protected void checkObjectTexture(GameObject object, CCTexture2D texture){
		if(!object.getSprite().getTexture().equals(texture)){
			object.getSprite().setTexture(texture);
		}
	}
	
	protected CGPoint worldPointToLayerPoint(CGPoint worldPoint){
		return CGPoint.ccp(worldPoint.x * PTM_RATIO + pointToWorld.x,
				worldPoint.y * PTM_RATIO + pointToWorld.y);
	}
	
	private CGPoint layerPointToWorldPoint(CGPoint layerPoint) {
		return CGPoint.ccp( (layerPoint.x - pointToWorld.x)/PTM_RATIO,
				(layerPoint.y - pointToWorld.y) / PTM_RATIO);
	}
	
	@Override
	public boolean ccTouchesBegan(MotionEvent event) {
		Log.d(TAG, "touch began");
		CGPoint convertedLocation = CCDirector.sharedDirector().convertToGL(CGPoint.make(event.getX(), event.getY()));
		Log.d(TAG, "coordinate = " + convertedLocation);
		Log.d(TAG, mouse.getSprite().getBoundingBox().toString());
		if(isPointInMouse(convertedLocation)){
			Log.d(TAG, "touch on mouse");
			dragMouse = true;
		}
		return super.ccTouchesBegan(event);
	}
	
	private boolean isPointInMouse(CGPoint point){
		return CGRect.containsPoint(mouse.getSprite().getBoundingBox(), point);
	}
	
	@Override
	public boolean ccTouchesMoved(MotionEvent event) {
		Log.d(TAG, "touch moved");
		if(dragMouse){
			Log.d(TAG, "touch move mouse");
			CGPoint p = CCDirector.sharedDirector().convertToGL(CGPoint.make(event.getX(), event.getY()));

			p = layerPointToWorldPoint(p);
			
			if(!world.IsPointPossible(p)){
				p = world.MakeNearestPossiblePoint(p);
			}

			mouse.setPosition(p);
			mouse.getSprite().setPosition(worldPointToLayerPoint(p));
		}
		return super.ccTouchesMoved(event);
	}

	@Override
	public boolean ccTouchesEnded(MotionEvent event) {
		Log.d(TAG, "touch end");
		dragMouse = false;
		return super.ccTouchesEnded(event);
	}
	
	private static final String TAG = GameActivity.TAG;
	private static final long extraTime = 3000;
	private static float MOUSE_WIDTH = 0.2f;
	private static float CAT_WIDTH = 0.4f;
	//XXX:width и height стоит объединить в size
	//room size
	private static float ROOM_WIDTH = 4.0f;
	private static float BOTTLE_WIDTH = 0.5f;
	private static float ROOM_HEIGHT = 3.0f;
	//XXX:много внутренних переменных, что является признаком плохого кода
	private float PTM_RATIO;
	private GameWorld world;
	private GameObject mouse;
	private boolean dragMouse = false;
	private long lastTime;
	private long extraRecharge;
	
	//XXX: не удобно хранить текстуры отдельно от объекта
	//так как например у нас будет две разных кошки рыжая и черная, => 4 разных структуры
	//мы хотим поменять текстуру на кошку идущую например вправо, мы знаем что это правая текстура
	//но не знаем для рыжей или черной кошки, GameObject не содержит инфомции об этом
	//если спрайт может иметь несколько текстур, они должны быть неразрывно связаны с ним еще при его создании
	//думаю, можно использовать CCSpriteSheet для решения этой проблемы
	private CCTexture2D catRedRightTexture;
	private CCTexture2D catRedLeftTexture;
	
	private CCTexture2D mouseTexture;
	private CCTexture2D milkTexture;

	//Point to world
	private CGPoint pointToWorld = CGPoint.ccp(0.0f, 0.0f);
	//Label
	private CCLabel lbl;

	private int NUMBER_OF_CATS = 2;
	private long SCORE = 0;
}
